home *** CD-ROM | disk | FTP | other *** search
/ Amiga Developer CD 2.1 / Amiga Developer CD v2.1.iso / NDK / NDK_3.5 / Tutorials / ARexx / Host / Host.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-10-03  |  40.1 KB  |  1,861 lines

  1. /*
  2.  * $Id$
  3.  *
  4.  * ARexx host demo
  5.  *
  6.  * :ts=8
  7.  */
  8.  
  9. /****************************************************************************/
  10.  
  11. #include <dos/dosextens.h>
  12. #include <dos/dostags.h>
  13. #include <dos/rdargs.h>
  14.  
  15. #include <exec/execbase.h>
  16. #include <exec/memory.h>
  17.  
  18. #include <rexx/rxslib.h>
  19. #include <rexx/errors.h>
  20.  
  21. #include <clib/rexxsyslib_protos.h>
  22. #include <clib/utility_protos.h>
  23. #include <clib/exec_protos.h>
  24. #include <clib/alib_protos.h>
  25. #include <clib/dos_protos.h>
  26.  
  27. #include <pragmas/rexxsyslib_pragmas.h>
  28. #include <pragmas/utility_pragmas.h>
  29. #include <pragmas/exec_pragmas.h>
  30. #include <pragmas/dos_pragmas.h>
  31.  
  32. #include <string.h>
  33. #include <stdio.h>
  34.  
  35. /****************************************************************************/
  36.  
  37.     /* We define a bunch of data structures here. While the basic work
  38.      * of maintaining the Rexx host will work without wrapping it neatly
  39.      * into a special data structure, it certainly helps.
  40.      */
  41.  
  42. struct RexxHost
  43. {
  44.     APTR            UserData;
  45.     struct CmdFunc *    CommandTable;
  46.  
  47.     struct MsgPort *    GlobalPort;
  48.     UBYTE            PortName[1];
  49. };
  50.  
  51.     /* This is for parsing commands the ARexx port receives. We employ
  52.      * the dos.library/ReadArgs parser for this purpose.
  53.      */
  54.  
  55. struct RexxParseData
  56. {
  57.     struct RDArgs *    ReadArgs;
  58.     UBYTE        Line[512];    /* We allow for commands to be up to
  59.                      * 511 characters long. This is far
  60.                      * below the maximum string length
  61.                      * limit ARexx imposed (32768
  62.                      * characters) but should be more
  63.                      * than sufficient for normal
  64.                      * applications.
  65.                      */
  66.     STRPTR        Args[32];    /* Up to 32 command arguments are
  67.                      * allowed.
  68.                      */
  69. };
  70.  
  71.     /* A table to map command names to functions which
  72.      * will handle them.
  73.      */
  74.  
  75. struct CmdFunc
  76. {
  77.     STRPTR CommandName;    /* Name of the command. */
  78.     STRPTR Template;    /* The command template in standard
  79.                  * AmigaDOS syntax.
  80.                  */
  81.  
  82.         /* Pointer to the function which implements this command. */
  83.  
  84.     LONG (*Function)(APTR,struct RexxMsg *,STRPTR *);
  85. };
  86.  
  87.     /* The local data used by the functions the host supports. */
  88.  
  89. struct LocalData
  90. {
  91.     struct CmdFunc *    CommandTable;    /* Pointer to table of commands. */
  92.     struct MsgPort *    GlobalPort;    /* Copied from RexxHost. */
  93.     STRPTR            PortName;    /* Copied from RexxHost. */
  94.     ULONG            Usage;        /* Usage count for Rx command. */
  95.     BOOL            Terminate;    /* Set to TRUE if host should exit. */
  96. };
  97.  
  98.     /* This is a custom message the Rx command will need if the
  99.      * "CONSOLE" keyword is used.
  100.      */
  101.  
  102. struct ChildMessage
  103. {
  104.     struct Message        Message;    /* Common message header. */
  105.  
  106.     struct RexxMsg *    RexxMsg;    /* The client message. */
  107.     STRPTR            PortName;    /* Copied from LocalData. */
  108.     UBYTE            Command[1];    /* Command to execute. */
  109. };
  110.  
  111. /****************************************************************************/
  112.  
  113.     /* Defined and initialized by compiler startup code. */
  114.  
  115. extern struct Library *    SysBase;
  116. extern struct Library *    DOSBase;
  117.  
  118.     /* To be initialized by this program. */
  119.  
  120. struct Library * RexxSysBase;
  121. struct Library * UtilityBase;
  122.  
  123. /****************************************************************************/
  124.  
  125. /****** Host/CreateRexxHost **************************************************
  126. *
  127. *   NAME
  128. *       CreateRexxHost -- Create a public message port for use as an ARexx
  129. *           host address.
  130. *
  131. *   SYNOPSIS
  132. *       Host = CreateRexxHost(BaseName,Index,CommandTable,UserData)
  133. *
  134. *       struct RexxHost *CreateRexxHost(const STRPTR BaseName,ULONG Index,
  135. *           const struct CmdFunc *CommandTable,const APTR UserData);
  136. *
  137. *   FUNCTION
  138. *       You supply a name and a project index, this routine will set up
  139. *       a public message port for use as an ARexx host address for you
  140. *       with the name you provided. The name will be checked for validity
  141. *       (it may not hold blank spaces or tabs) and converted to all
  142. *       upper case letters. If a non-zero project index number is provided,
  143. *       a numeric extension will be added to the message port name.
  144. *       This routine will try to make sure that the message port will bear
  145. *       a unique name. For your convenience, you can have the host data
  146. *       structure with a user data pointer.
  147. *
  148. *       Please keep the length of the port name down to up to 20 characters,
  149. *       many users prefer short host names. This routine will reject names
  150. *       longer than 80 characters.
  151. *
  152. *       The name is converted to all upper case characters to make it easier
  153. *       to access the host name in ARexx scripts. Lower case names require
  154. *       quoting whereas upper case names do not.
  155. *
  156. *   INPUTS
  157. *       BaseName - Name of message port to create; the name may not
  158. *           contain blank spaces or tabs and will be converted to
  159. *           all upper case letters. The name you provide may be up to
  160. *           80 characters long.
  161. *
  162. *       Index - Project index number; if nonzero will cause a numeric
  163. *           extension to be added to the port name, i.e. for an index
  164. *           number of 1 the name "BASENAME" will become "BASENAME.1".
  165. *
  166. *       CommandTable - Points to the first element of the array in
  167. *           which command names, templates and functions are stored.
  168. *
  169. *       UserData - User data pointer which will be copied into the
  170. *           RexxHost structure.
  171. *
  172. *   RESULT
  173. *       Host - Pointer to RexxHost created or NULL if creation failed.
  174. *           If NULL is returned you can retrieve an error code through
  175. *           dos.library/IoErr.
  176. *
  177. *   SEE ALSO
  178. *       exec.library/AddPort
  179. *       exec.library/CreateMsgPort
  180. *       dos.library/IoErr
  181. *
  182. ******************************************************************************
  183. *
  184. */
  185.  
  186. struct RexxHost *
  187. CreateRexxHost(
  188.     const STRPTR        BaseName,
  189.     ULONG            Index,
  190.     const struct CmdFunc *    CommandTable,
  191.     const APTR        UserData)
  192. {
  193.     struct Process * ThisProcess;
  194.     struct RexxHost * Host;
  195.     UBYTE PortName[90];
  196.     LONG Error;
  197.     LONG Size;
  198.     LONG Len;
  199.     LONG i;
  200.  
  201.         /* No host created yet. */
  202.  
  203.     Host = NULL;
  204.  
  205.         /* No error so far. */
  206.  
  207.     Error = 0;
  208.  
  209.         /* Check for NULL name. */
  210.  
  211.     if(BaseName == NULL)
  212.         Error = ERROR_REQUIRED_ARG_MISSING;
  213.  
  214.         /* Check for illegal characters. */
  215.  
  216.     if(Error == 0)
  217.     {
  218.         Len = strlen(BaseName);
  219.  
  220.         for(i = 0 ; (Error == 0) && (i < Len) ; i++)
  221.         {
  222.             if(BaseName[i] == ' ' || BaseName[i] == '\t')
  223.                 Error = ERROR_INVALID_COMPONENT_NAME;
  224.         }
  225.     }
  226.  
  227.         /* Check the name length. */
  228.  
  229.     if(Error == 0)
  230.     {
  231.         if(Len > 80)
  232.             Error = ERROR_OBJECT_TOO_LARGE;
  233.     }
  234.  
  235.         /* Create the RexxHost data. */
  236.  
  237.     if(Error == 0)
  238.     {
  239.             /* Add the index number if provided. */
  240.  
  241.         if(Index != 0)
  242.             sprintf(PortName,"%s.%ld",BaseName,Index);
  243.         else
  244.             strcpy(PortName,BaseName);
  245.  
  246.             /* Convert the port name to all upper case
  247.              * characters.
  248.              */
  249.  
  250.         for(i = 0 ; i < Len ; i++)
  251.             PortName[i] = ToUpper(PortName[i]);
  252.  
  253.             /* Allocate space for the RexxHost. The name
  254.              * requires a terminating 0-byte which is included
  255.              * in the RexxHost structure.
  256.              */
  257.  
  258.         Size = sizeof(struct RexxHost) + strlen(PortName);
  259.  
  260.         Host = (struct RexxHost *)AllocVec(Size,
  261.             MEMF_ANY|MEMF_CLEAR|MEMF_PUBLIC);
  262.  
  263.             /* Did we get the memory we wanted? */
  264.  
  265.         if(Host == NULL)
  266.             Error = ERROR_NO_FREE_STORE;
  267.         else
  268.         {
  269.                 /* Set up the message port. */
  270.  
  271.             Host->GlobalPort = CreateMsgPort();
  272.  
  273.                 /* Did we get the port? If the creation
  274.                  * failed we either ran out of free
  275.                  * signal bits or memory.
  276.                  */
  277.  
  278.             if(Host->GlobalPort == NULL)
  279.                 Error = ERROR_NO_FREE_STORE;
  280.             else
  281.             {
  282.                     /* Fill in the host data. Since the
  283.                      * port will be searched for often
  284.                      * by the ARexx resident process we
  285.                      * assign it a priority of 1.
  286.                      */
  287.  
  288.                 strcpy(Host->PortName,PortName);
  289.  
  290.                 Host->GlobalPort->mp_Node.ln_Name =
  291.                     Host->PortName;
  292.  
  293.                 Host->GlobalPort->mp_Node.ln_Pri = 1;
  294.  
  295.                 Host->CommandTable = CommandTable;
  296.                 Host->UserData = UserData;
  297.             }
  298.         }
  299.     }
  300.  
  301.         /* Try to add the message port. */
  302.  
  303.     if(Error == 0)
  304.     {
  305.             /* We need to turn off multitasking for a moment
  306.              * while we access the global message port list.
  307.              */
  308.  
  309.         Forbid();
  310.  
  311.             /* Check if there already is a message port by
  312.              * the name we just used.
  313.              */
  314.  
  315.         if(FindPort(Host->PortName))
  316.             Error = ERROR_OBJECT_EXISTS;
  317.  
  318.             /* If there is no such port yet, add the new
  319.              * message port to the public list.
  320.              */
  321.  
  322.         if(Error == 0)
  323.             AddPort(Host->GlobalPort);
  324.  
  325.             /* Enable multitasking again. */
  326.  
  327.         Permit();
  328.     }
  329.  
  330.         /* Did we succeed? */
  331.  
  332.     if(Error == 0)
  333.         return(Host);
  334.     else
  335.     {
  336.             /* Clean up the host data. */
  337.  
  338.         if(Host != NULL)
  339.         {
  340.                 /* Delete the message port. This call
  341.                  * will succeed even if the pointer is
  342.                  * NULL.
  343.                  */
  344.  
  345.             DeleteMsgPort(Host->GlobalPort);
  346.  
  347.                 /* Free the memory allocated for the
  348.                  * host data.
  349.                  */
  350.  
  351.             FreeVec(Host);
  352.         }
  353.  
  354.             /* Obtain the control structure for this Process
  355.              * or Task. We need to set an error code, but the
  356.              * system call we will make can only be made by
  357.              * a Process.
  358.              */
  359.  
  360.         ThisProcess = (struct Process *)FindTask(NULL);
  361.  
  362.             /* If a process is making the call, set the
  363.              * error code so it can later be retrieved
  364.              * through IoErr().
  365.              */
  366.  
  367.         if(ThisProcess->pr_Task.tc_Node.ln_Type == NT_PROCESS)
  368.             SetIoErr(Error);
  369.  
  370.             /* Return failure. */
  371.  
  372.         return(NULL);
  373.     }
  374. }
  375.  
  376. /****** Host/DeleteRexxHost **************************************************
  377. *
  378. *   NAME
  379. *       DeleteRexxHost -- Remove a rexx host port previously created by
  380. *           CreateRexxHost.
  381. *
  382. *   SYNOPSIS
  383. *       DeleteRexxHost(Host)
  384. *
  385. *       VOID DeleteRexxHost(struct RexxHost *);
  386. *
  387. *   FUNCTION
  388. *       Before your application exits, call this routine on every rexx host
  389. *       environment you created with CreateRexxHost(). If you forget to do so
  390. *       your application will leave unused memory and, in case it was
  391. *       run from Shell, signal bits allocated which can never be reclaimed.
  392. *
  393. *   INPUTS
  394. *       Host - Result of CreateRexxHost() call. May be NULL in which case
  395. *           this routine will do nothing.
  396. *
  397. *   RESULT
  398. *       None
  399. *
  400. *   SEE ALSO
  401. *       exec.library/DeleteMsgPort
  402. *       exec.library/RemPort
  403. *       CreateRexxHost
  404. *
  405. ******************************************************************************
  406. *
  407. */
  408.  
  409. VOID
  410. DeleteRexxHost(struct RexxHost * Host)
  411. {
  412.     struct RexxMsg * Message;
  413.  
  414.         /* Did we get a valid host pointer? */
  415.  
  416.     if(Host != NULL)
  417.     {
  418.             /* Turn off multitasking while we remove the
  419.              * public message port.
  420.              */
  421.  
  422.         Forbid();
  423.  
  424.             /* Remove the message port. */
  425.  
  426.         RemPort(Host->GlobalPort);
  427.  
  428.             /* Now enable multitasking again. */
  429.  
  430.         Permit();
  431.  
  432.             /* Remove all pending rexx messages from the
  433.              * port and reply them to their owners.
  434.              */
  435.  
  436.         while((Message = (struct RexxMsg *)GetMsg(Host->GlobalPort))
  437.             != NULL)
  438.         {
  439.                 /* Reject all messages. */
  440.  
  441.             Message->rm_Result1 = RC_FATAL;
  442.  
  443.             ReplyMsg((struct Message *)Message);
  444.         }
  445.  
  446.             /* Delete the message port. */
  447.  
  448.         DeleteMsgPort(Host->GlobalPort);
  449.  
  450.             /* Free the memory allocated. */
  451.  
  452.         FreeVec(Host);
  453.     }
  454. }
  455.  
  456. /****************************************************************************/
  457.  
  458. /****** Host/CreateRexxParseData *********************************************
  459. *
  460. *   NAME
  461. *       CreateRexxParseData -- Create working data for command parsing.
  462. *
  463. *   SYNOPSIS
  464. *       Data = CreateRexxParseData()
  465. *
  466. *       struct RexxParseData *CreateRexxParseData(VOID);
  467. *
  468. *   FUNCTION
  469. *       The commands the host receives from the Rexx resident process will
  470. *       need further processing. This is what the command parser will do,
  471. *       but it needs environment data to work with. This routine allocates
  472. *       the work space it needs.
  473. *
  474. *   INPUTS
  475. *       None
  476. *
  477. *   RESULT
  478. *       Data - Pointer to RexxParseData structure or NULL if it could not
  479. *           be allocated. If NULL is returned the error code can be
  480. *           obtained through IoErr().
  481. *
  482. *   SEE ALSO
  483. *       dos.library/AllocDosObject
  484. *       dos.library/IoErr
  485. *
  486. ******************************************************************************
  487. *
  488. */
  489.  
  490. struct RexxParseData *
  491. CreateRexxParseData(VOID)
  492. {
  493.     struct Process * ThisProcess;
  494.     struct RexxParseData * Data;
  495.     LONG Error;
  496.  
  497.         /* No data was allocated yet. */
  498.  
  499.     Data = NULL;
  500.  
  501.         /* No error has occured yet. */
  502.  
  503.     Error = 0;
  504.  
  505.         /* Allocate the basic parser data. */
  506.  
  507.     Data = AllocVec(sizeof(struct RexxParseData),MEMF_ANY|MEMF_CLEAR);
  508.  
  509.         /* Did we get the memory? */
  510.  
  511.     if(Data == NULL)
  512.         Error = ERROR_NO_FREE_STORE;
  513.  
  514.         /* Allocate the ReadArgs data. */
  515.  
  516.     if(Error == 0)
  517.     {
  518.             /* AllocDosObjectTagList is a dos.library call which
  519.              * can be made by a Task.
  520.              */
  521.  
  522.         Data->ReadArgs = AllocDosObjectTagList(DOS_RDARGS,NULL);
  523.  
  524.             /* Did we get the data? */
  525.  
  526.         if(Data->ReadArgs == NULL)
  527.             Error = ERROR_NO_FREE_STORE;
  528.     }
  529.  
  530.         /* Did we succeed? */
  531.  
  532.     if(Error == 0)
  533.         return(Data);
  534.     else
  535.     {
  536.             /* Clean up the parser data; in this case failure
  537.              * means either the ReadArgs data or the data
  538.              * structure itself could not be allocated. Thus,
  539.              * we only need to take care of the data, a single
  540.              * call to FreeVec() is sufficient. Since FreeVec()
  541.              * can be called with a NULL parameter we do not
  542.              * need to check if the data pointer is valid.
  543.              */
  544.  
  545.         FreeVec(Data);
  546.  
  547.             /* Obtain the control structure for this Process
  548.              * or Task. We need to set an error code, but the
  549.              * system call we will make can only be made by
  550.              * a Process.
  551.              */
  552.  
  553.         ThisProcess = (struct Process *)FindTask(NULL);
  554.  
  555.             /* If a process is making the call, set the
  556.              * error code so it can later be retrieved
  557.              * through IoErr().
  558.              */
  559.  
  560.         if(ThisProcess->pr_Task.tc_Node.ln_Type == NT_PROCESS)
  561.             SetIoErr(Error);
  562.  
  563.             /* Return failure. */
  564.  
  565.         return(NULL);
  566.     }
  567. }
  568.  
  569. /****** Host/DeleteRexxParseData *********************************************
  570. *
  571. *   NAME
  572. *       DeleteRexxParseData -- Free data allocated by CreateRexxParseData.
  573. *
  574. *   SYNOPSIS
  575. *       DeleteRexxParseData(Data)
  576. *
  577. *       VOID DeleteRexxParseData(struct RexxParseData *);
  578. *
  579. *   FUNCTION
  580. *       Before your program exits it should call this routine to release
  581. *       all the memory allocated for command parsing. Otherwise it will lose
  582. *       memory that can never be reclaimed.
  583. *
  584. *   INPUTS
  585. *       Data - Pointer to RexxParseData structure created by
  586. *           CreateRexxParseData. May be NULL in which case this routine
  587. *           does nothing.
  588. *
  589. *   RESULT
  590. *       None
  591. *
  592. *   SEE ALSO
  593. *       dos.library/FreeDosObject
  594. *
  595. ******************************************************************************
  596. *
  597. */
  598.  
  599. VOID
  600. DeleteRexxParseData(struct RexxParseData * Data)
  601. {
  602.         /* Did we get a valid data pointer? */
  603.  
  604.     if(Data != NULL)
  605.     {
  606.             /* Clean up the parser data. */
  607.  
  608.         if(Data->ReadArgs != NULL)
  609.             FreeDosObject(DOS_RDARGS,Data->ReadArgs);
  610.  
  611.             /* Free the memory. */
  612.  
  613.         FreeVec(Data);
  614.     }
  615. }
  616.  
  617. /****** Host/ReturnRexxMsg ***************************************************
  618. *
  619. *   NAME
  620. *       ReturnRexxMsg -- Return a Rexx command message to the sender,
  621. *           including a result code.
  622. *
  623. *   SYNOPSIS
  624. *       ErrorCode = ReturnRexxMsg(Message,Result)
  625. *
  626. *       LONG ReturnRexxMsg(struct RexxMsg *Message,const STRPTR Result);
  627. *
  628. *   FUNCTION
  629. *       Clients addressing the host send special messages carrying the
  630. *       commands and command arguments to the host message port. After
  631. *       processing the commands the host needs to return these messages.
  632. *
  633. *       This routine cannot be used to return error or failure messages
  634. *       to the client. For this purpose use ReturnErrorMsg() instead.
  635. *
  636. *   INPUTS
  637. *       Message - Pointer to RexxMsg received from client.
  638. *
  639. *       Result - Pointer to string that should be transmitted to
  640. *           the client as the command result. This may be NULL in
  641. *           which case no result string will be transmitted.
  642. *
  643. *   RESULT
  644. *       Error - 0 for success, any other value indicates failure.
  645. *
  646. *   SEE ALSO
  647. *       exec.library/ReplyMsg
  648. *       rexxsyslib.library/CreateArgstring
  649. *       ReturnErrorMsg
  650. *
  651. ******************************************************************************
  652. *
  653. */
  654.  
  655. LONG
  656. ReturnRexxMsg(struct RexxMsg * Message,const STRPTR Result)
  657. {
  658.     STRPTR ResultString;
  659.     LONG ErrorCode;
  660.  
  661.         /* No error has occured yet. */
  662.  
  663.     ErrorCode = 0;
  664.  
  665.         /* Set up the RexxMsg to return no error. */
  666.  
  667.     Message->rm_Result1 = RC_OK;
  668.     Message->rm_Result2 = 0;
  669.  
  670.         /* Check if the command should return a result. */
  671.  
  672.     if((Message->rm_Action & RXFF_RESULT) && Result != NULL)
  673.     {
  674.             /* To return the result string we need to make
  675.              * a copy for ARexx to use.
  676.              */
  677.  
  678.         if(ResultString = CreateArgstring(Result,strlen(Result)))
  679.         {
  680.                 /* Put the string into the secondary
  681.                  * result field.
  682.                  */
  683.  
  684.             Message->rm_Result2 = (LONG)ResultString;
  685.         }
  686.         else
  687.         {
  688.                 /* No memory available. */
  689.  
  690.             ErrorCode = ERR10_003;
  691.         }
  692.     }
  693.  
  694.         /* Reply the message, regardless of the error code. */
  695.  
  696.     ReplyMsg((struct Message *)Message);
  697.  
  698.     return(ErrorCode);
  699. }
  700.  
  701. /****** Host/ReturnErrorMsg **************************************************
  702. *
  703. *   NAME
  704. *       ReturnErrorMsg -- Return a Rexx command message to the sender and
  705. *           indicate that the command failed.
  706. *
  707. *   SYNOPSIS
  708. *       ReturnErrorMsg(Message,PortName,Error)
  709. *
  710. *       VOID ReturnErrorMsg(struct RexxMsg *Message,const STRPTR PortName,
  711. *           LONG Error)
  712. *
  713. *   FUNCTION
  714. *       This routine permits to return a command message sent by a client
  715. *       and set an error code the client to send the message can query.
  716. *       The error code is stored in a variable whose name is made up from
  717. *       the name of the host name and the prefix ".LASTERROR". Thus,
  718. *       for the host name "HOST" the variable becomes "HOST.LASTERROR".
  719. *
  720. *   INPUTS
  721. *       Message - Pointer to RexxMsg received from client.
  722. *
  723. *       PortName - Name of the host the message was sent to.
  724. *
  725. *       Error - Error code to return.
  726. *
  727. *   RESULT
  728. *       None
  729. *
  730. *   SEE ALSO
  731. *       exec.library/ReplyMsg
  732. *       amiga.lib/SetRexxVar
  733. *
  734. ******************************************************************************
  735. *
  736. */
  737.  
  738. VOID
  739. ReturnErrorMsg(
  740.     struct RexxMsg *    Message,
  741.     const STRPTR        PortName,
  742.     LONG            Error)
  743. {
  744.     UBYTE VarName[90];
  745.     UBYTE Value[12];
  746.  
  747.         /* To signal an error the rc_Result1
  748.          * entry of the RexxMsg needs to be set to
  749.          * RC_ERROR. Unfortunately, we cannot convey
  750.          * the more meaningful error code through
  751.          * this interface which is why we set a
  752.          * Rexx variable to the error number. The
  753.          * Rexx script can then take a look at this
  754.          * variable and decide which further steps
  755.          * it should take.
  756.          */
  757.  
  758.     Message->rm_Result1 = RC_ERROR;
  759.  
  760.         /* Turn the error number into a string as
  761.          * ARexx only deals with strings.
  762.          */
  763.  
  764.     sprintf(Value,"%ld",Error);
  765.  
  766.         /* Build the name of the variable to set to
  767.          * the error number. We will use the name of
  768.          * the host name and append ".LASTERROR".
  769.          */
  770.  
  771.     sprintf(VarName,"%s.%s",PortName,"LASTERROR");
  772.  
  773.         /* Now set the variable to the error code.
  774.          * We use the RVI (Rexx Variables Interface)
  775.          * routine for this purpose.
  776.          */
  777.  
  778.     SetRexxVar(Message,VarName,Value,strlen(Value));
  779.  
  780.     ReplyMsg((struct Message *)Message);
  781. }
  782.  
  783. /****** Host/HandleRexxMsg ***************************************************
  784. *
  785. *   NAME
  786. *       HandleRexxMsg -- Process incoming rexx message and invoke the
  787. *           apropriate function handler.
  788. *
  789. *   SYNOPSIS
  790. *       HandleRexxMsg(Host,Data,Message)
  791. *
  792. *       VOID HandleRexxMsg(const struct RexxHost *Host,
  793. *           struct RexxParseData *Data,struct RexxMsg *Message);
  794. *
  795. *   FUNCTION
  796. *       This routine prepares the ground for the final command execution.
  797. *       It tries to find the command the Rexx message should invoke and
  798. *       parses the command arguments. If a matching command could be found
  799. *       and the arguments are correct the corresponding function is invoked.
  800. *       When control has passed through this routine the Rexx message will
  801. *       have been replied.
  802. *
  803. *   INPUTS
  804. *       Host - ARexx Host environment as created by CreateRexxHost
  805. *
  806. *       Data - Parser data as created by CreateParseData
  807. *
  808. *       Message - Pointer to RexxMsg received from client.
  809. *
  810. *   NOTES
  811. *       This routine should only be called by a Process.
  812. *
  813. *   RESULT
  814. *       None
  815. *
  816. *   SEE ALSO
  817. *       dos.library/ReadArgs
  818. *       CreateParseData
  819. *       CreateRexxHost
  820. *
  821. ******************************************************************************
  822. *
  823. */
  824.  
  825. VOID
  826. HandleRexxMsg(
  827.     const struct RexxHost *    Host,
  828.     struct RexxParseData *    Data,
  829.     struct RexxMsg *    Message)
  830. {
  831.     struct CmdFunc * Table;
  832.     struct CmdFunc * CmdFunc;
  833.     STRPTR Command;
  834.     STRPTR Arguments;
  835.     LONG Error;
  836.     LONG Len;
  837.     LONG i;
  838.  
  839.         /* This is where the entire command string is stored,
  840.          * including following arguments.
  841.          */
  842.  
  843.     Command = Message->rm_Args[0];
  844.  
  845.         /* Skip leading blanks. */
  846.  
  847.     while(*Command == ' ' || *Command == '\t')
  848.         Command++;
  849.  
  850.         /* Now determine the length of the command. */
  851.  
  852.     Len = 0;
  853.  
  854.     while(Command[Len])
  855.     {
  856.         if(Command[Len] == '\t' || Command[Len] == ' ')
  857.             break;
  858.         else
  859.             Len++;
  860.     }
  861.  
  862.         /* Now we need to check if the command we received
  863.          * can be handled by one of the routines in the
  864.          * command table.
  865.          */
  866.  
  867.     Table = Host->CommandTable;
  868.     CmdFunc = NULL;
  869.  
  870.     for(i = 0 ; Table[i].CommandName != NULL ; i++)
  871.     {
  872.         if((strlen(Table[i].CommandName) == Len) && (Strnicmp(Command,Table[i].CommandName,Len) == 0))
  873.         {
  874.             CmdFunc = &Table[i];
  875.             break;
  876.         }
  877.     }
  878.  
  879.         /* If no matching command could be found, return
  880.          * an error.
  881.          */
  882.  
  883.     if(CmdFunc == NULL)
  884.         Error = ERROR_OBJECT_NOT_FOUND;
  885.     else
  886.     {
  887.             /* Reset the argument pointers. */
  888.  
  889.         memset(Data->Args,0,sizeof(Data->Args));
  890.  
  891.             /* Separate the arguments from the command;
  892.              * first skip the leading blanks.
  893.              */
  894.  
  895.         Arguments = &Command[Len];
  896.  
  897.         while(*Arguments == '\t' || *Arguments == ' ')
  898.             Arguments++;
  899.  
  900.             /* Remove trailing blanks. */
  901.  
  902.         Len = strlen(Arguments);
  903.  
  904.         while((Len > 0) &&
  905.             (Arguments[Len - 1] == '\t' || Arguments[Len - 1] == ' '))
  906.                 Len--;
  907.  
  908.             /* We only allow for 510 characters of argument data. */
  909.  
  910.         if(Len > 510)
  911.             Len = 510;
  912.  
  913.             /* Put the arguments into the parser buffer. */
  914.  
  915.         if(Len > 0)
  916.             CopyMem(Arguments,Data->Line,Len);
  917.  
  918.             /* Add the line feed and the null-termination. */
  919.  
  920.         Data->Line[Len] = '\n';
  921.         Data->Line[Len+1] = 0;
  922.  
  923.             /* Set up the parser to read from the argument
  924.              * buffer.
  925.              */
  926.  
  927.         Data->ReadArgs->RDA_Source.CS_Buffer = Data->Line;
  928.         Data->ReadArgs->RDA_Source.CS_Length = Len + 1;
  929.         Data->ReadArgs->RDA_Source.CS_CurChr = 0;
  930.         Data->ReadArgs->RDA_Flags |= RDAF_NOPROMPT;
  931.  
  932.             /* Run the arguments through the parser. This will
  933.              * set up the Data->Args array.
  934.              */
  935.  
  936.         if(ReadArgs(CmdFunc->Template,(LONG *)Data->Args,Data->ReadArgs))
  937.         {
  938.                 /* Invoke the command. */
  939.  
  940.             Error = (*CmdFunc->Function)(Host->UserData,Message,Data->Args);
  941.  
  942.                 /* Free the parser data. */
  943.  
  944.             FreeArgs(Data->ReadArgs);
  945.         }
  946.         else
  947.             Error = IoErr();
  948.     }
  949.  
  950.         /* If an error occured, return the message now.
  951.          * In any other case the command handler will have
  952.          * taken care of it already.
  953.          */
  954.  
  955.     if(Error)
  956.         ReturnErrorMsg(Message,Host->PortName,Error);
  957. }
  958.  
  959. /****************************************************************************/
  960.  
  961.     /* The following routines implement the 15 mandatory ARexx commands
  962.      * every ARexx host should support, as per the "Amiga User Interface
  963.      * Style Guide". With the exception of the FAULT, HELP, QUIT and RX
  964.      * commands none of the example commands do any real work. They just
  965.      * print their arguments and return an error code.
  966.      *
  967.      * Every routine is invoked with three parameters:
  968.      *
  969.      * UserData -- The user data pointer passed to CreateRexxHost.
  970.      *
  971.      * Message -- Pointer to the RexxMsg received from the client.
  972.      *
  973.      * Args -- Pointer to an array of 32 string pointers, as set up
  974.      *         by the dos.library/ReadArgs parser.
  975.      *
  976.      * Each routine has to return an error code. If the code returned
  977.      * is nonzero the command dispatcher in HandleRexxMsg() will
  978.      * reply the RexxMsg. If the code is zero, the routine is
  979.      * responsible for replying the RexxMsg.
  980.      */
  981.  
  982. LONG
  983. Cmd_Fault(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  984. {
  985.     enum    { ARG_NUMBER };
  986.  
  987.     UBYTE ErrorText[100];
  988.     LONG Number;
  989.  
  990.     Number = *(LONG *)Args[ARG_NUMBER];
  991.  
  992.     Printf("FAULT %d",Number);
  993.     Printf("\n");
  994.  
  995.         /* Obtain the AmigaDOS error text corresponding
  996.          * to the error code. In your own applications
  997.          * you would want to supply descriptive texts
  998.          * for each error code your program supports
  999.          * in addition to the standard AmigaDOS set.
  1000.          */
  1001.  
  1002.     if(Fault(Number,"",ErrorText,99) > 0)
  1003.     {
  1004.         ReturnRexxMsg(Message,&ErrorText[2]);
  1005.  
  1006.         return(0);
  1007.     }
  1008.     else
  1009.         return(ERROR_OBJECT_NOT_FOUND);
  1010. }
  1011.  
  1012. LONG
  1013. Cmd_Help(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1014. {
  1015.     enum    { ARG_COMMAND,ARG_PROMPT };
  1016.  
  1017.     struct LocalData *LocalData;
  1018.     struct CmdFunc *Table;
  1019.     STRPTR String;
  1020.     STRPTR Command;
  1021.     LONG Len;
  1022.     LONG i;
  1023.  
  1024.     Printf("HELP");
  1025.  
  1026.     if(Args[ARG_PROMPT] != NULL)
  1027.         Printf(" PROMPT");
  1028.  
  1029.     Printf("\n");
  1030.  
  1031.         /* The "PROMPT" switch should activate a graphical
  1032.          * help system, which this implementation does not
  1033.          * support yet.
  1034.          */
  1035.  
  1036.     if(Args[ARG_PROMPT])
  1037.         return(ERROR_ACTION_NOT_KNOWN);
  1038.     else
  1039.     {
  1040.         LocalData = (struct LocalData *)UserData;
  1041.  
  1042.         Table = LocalData->CommandTable;
  1043.  
  1044.             /* If no command is specified then we should
  1045.              * return a list of all the commands this host
  1046.              * supports.
  1047.              */
  1048.  
  1049.         if(Args[ARG_COMMAND] == NULL)
  1050.         {
  1051.                 /* Count the number of bytes to allocate
  1052.                  * for a list of all commands.
  1053.                  */
  1054.  
  1055.             for(i = 0, Len = 1 ; Table[i].CommandName ; i++)
  1056.                 Len += strlen(Table[i].CommandName) + 1;
  1057.  
  1058.                 /* Make room for the command list. */
  1059.  
  1060.             String = AllocVec(Len,MEMF_ANY);
  1061.  
  1062.             if(String == NULL)
  1063.                 return(ERROR_NO_FREE_STORE);
  1064.             else
  1065.             {
  1066.                     /* Start with an empty string. */
  1067.  
  1068.                 String[0] = 0;
  1069.  
  1070.                     /* Add all the commands to the list. */
  1071.  
  1072.                 for(i = 0 ; Table[i].CommandName ; i++)
  1073.                 {
  1074.                     strcat(String,Table[i].CommandName);
  1075.                     strcat(String," ");
  1076.                 }
  1077.  
  1078.                     /* Terminate the string. */
  1079.  
  1080.                 String[Len - 1] = 0;
  1081.  
  1082.                     /* Reply the RexxMsg and return the
  1083.                      * command list as the command result.
  1084.                      */
  1085.  
  1086.                 ReturnRexxMsg(Message,String);
  1087.  
  1088.                     /* Dispose of the temporary storage. */
  1089.  
  1090.                 FreeVec(String);
  1091.  
  1092.                 return(0);
  1093.             }
  1094.         }
  1095.         else
  1096.         {
  1097.                 /* So we should return the argument template
  1098.                  * of a specific command.
  1099.                  */
  1100.  
  1101.             Command = Args[ARG_COMMAND];
  1102.  
  1103.                 /* Try to find the command in the table. */
  1104.  
  1105.             for(i = 0 ; Table[i].CommandName ; i++)
  1106.             {
  1107.                     /* Is this the command we are
  1108.                      * looking for?
  1109.                      */
  1110.  
  1111.                 if(Stricmp(Table[i].CommandName,Command) == 0)
  1112.                 {
  1113.                         /* Get the argument template. */
  1114.  
  1115.                     String = Table[i].Template;
  1116.  
  1117.                         /* Is the template an empty
  1118.                          * string?
  1119.                          */
  1120.  
  1121.                     if(String[0] == '\0')
  1122.                     {
  1123.                         /* Provide an empty template. */
  1124.  
  1125.                         String = ",";
  1126.                     }
  1127.  
  1128.                         /* Reply the RexxMsg and return
  1129.                          * the template as the command
  1130.                          * result.
  1131.                          */
  1132.  
  1133.                     ReturnRexxMsg(Message,String);
  1134.  
  1135.                     return(0);
  1136.                 }
  1137.             }
  1138.  
  1139.                 /* No matching command was found. */
  1140.  
  1141.             return(ERROR_OBJECT_NOT_FOUND);
  1142.         }
  1143.     }
  1144. }
  1145.  
  1146. LONG
  1147. Cmd_Quit(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1148. {
  1149.     enum    { ARG_FORCE };
  1150.  
  1151.     struct LocalData *LocalData;
  1152.  
  1153.     Printf("QUIT");
  1154.  
  1155.     if(Args[ARG_FORCE] != NULL)
  1156.         Printf(" FORCE");
  1157.  
  1158.     Printf("\n");
  1159.  
  1160.     LocalData = (struct LocalData *)UserData;
  1161.  
  1162.         /* Flag the main message loop to exit. */
  1163.  
  1164.     LocalData->Terminate = TRUE;
  1165.  
  1166.     ReturnRexxMsg(Message,NULL);
  1167.  
  1168.     return(0);
  1169. }
  1170.  
  1171. /****** Host/DoRexxCommand ***************************************************
  1172. *
  1173. *   NAME
  1174. *       DoRexxCommand -- Send a command to the Rexx resident process.
  1175. *
  1176. *   SYNOPSIS
  1177. *       Error = DoRexxCommand(Command)
  1178. *
  1179. *       LONG DoRexxCommand(const STRPTR Command);
  1180. *
  1181. *   FUNCTION
  1182. *       A command is sent to the Rexx resident process. In this context
  1183. *       "command" either means the name of an ARexx script the resident
  1184. *       process is to load and execute or a small one-line program.
  1185. *
  1186. *   INPUTS
  1187. *       Command - Name of command to execute.
  1188. *
  1189. *   RESULT
  1190. *       Error - 0 for success, otherwise an error code.
  1191. *
  1192. *   SEE ALSO
  1193. *       rexxsyslib.library/CreateArgstring
  1194. *       rexxsyslib.library/DeleteArgstring
  1195. *       rexxsyslib.library/CreateRexxMsg
  1196. *       rexxsyslib.library/DeleteRexxMsg
  1197. *       exec.library/CreateMsgPort
  1198. *       exec.library/DeleteMsgPort
  1199. *       exec.library/FindPort
  1200. *       exec.library/PutMsg
  1201. *       exec.library/WaitPort
  1202. *
  1203. ******************************************************************************
  1204. *
  1205. */
  1206.  
  1207. LONG
  1208. DoRexxCommand(const STRPTR Command)
  1209. {
  1210.     struct MsgPort *ReplyPort;
  1211.     struct MsgPort *RexxPort;
  1212.     struct RexxMsg *RexxMsg;
  1213.     LONG Error;
  1214.  
  1215.         /* Create a temporary reply port for the RexxMsg
  1216.          * to build.
  1217.          */
  1218.  
  1219.     if(ReplyPort = CreateMsgPort())
  1220.     {
  1221.             /* Build a RexxMsg we can use. */
  1222.  
  1223.         if(RexxMsg = CreateRexxMsg(ReplyPort,NULL,NULL))
  1224.         {
  1225.                 /* The command goes into the first
  1226.                  * message argument.
  1227.                  */
  1228.  
  1229.             if(RexxMsg->rm_Args[0] = CreateArgstring(Command,strlen(Command)))
  1230.             {
  1231.                     /* Mark the message as a command. */
  1232.  
  1233.                 RexxMsg->rm_Action = RXCOMM;
  1234.  
  1235.                     /* Now try to find the resident process
  1236.                      * communications port. We need to
  1237.                      * turn off multitasking while we look
  1238.                      * for it and send the message.
  1239.                      */
  1240.  
  1241.                 Forbid();
  1242.  
  1243.                 if(RexxPort = FindPort(RXSDIR))
  1244.                 {
  1245.                     PutMsg(RexxPort,RexxMsg);
  1246.  
  1247.                     WaitPort(ReplyPort);
  1248.                     GetMsg(ReplyPort);
  1249.  
  1250.                     Error = 0;
  1251.                 }
  1252.                 else
  1253.                     Error = ERROR_OBJECT_NOT_FOUND;
  1254.  
  1255.                 Permit();
  1256.  
  1257.                 DeleteArgstring(RexxMsg->rm_Args[0]);
  1258.             }
  1259.             else
  1260.                 Error = ERROR_NO_FREE_STORE;
  1261.  
  1262.             DeleteRexxMsg(RexxMsg);
  1263.         }
  1264.         else
  1265.             Error = ERROR_NO_FREE_STORE;
  1266.  
  1267.         DeleteMsgPort(ReplyPort);
  1268.     }
  1269.     else
  1270.         Error = ERROR_NO_FREE_STORE;
  1271.  
  1272.     return(Error);
  1273. }
  1274.  
  1275.     /* RxEntry(VOID):
  1276.      *
  1277.      *    This is the entry point of a process that will get launched
  1278.      *    if the RX command is invoked with the "CONSOLE" parameter.
  1279.      */
  1280.  
  1281. VOID __saveds
  1282. RxEntry(VOID)
  1283. {
  1284.     struct Process *ThisProcess;
  1285.     BPTR Stream;
  1286.     struct ChildMessage *ChildMessage;
  1287.     struct RexxMsg *SyncMsg;
  1288.     LONG Error;
  1289.  
  1290.         /* Obtain current process identifier. */
  1291.  
  1292.     ThisProcess = (struct Process *)FindTask(NULL);
  1293.  
  1294.         /* Pick up the startup message. */
  1295.  
  1296.     WaitPort(&ThisProcess->pr_MsgPort);
  1297.     ChildMessage = (struct ChildMessage *)GetMsg(&ThisProcess->pr_MsgPort);
  1298.  
  1299.         /* Remember the RexxMsg if any. If nonzero, the RX command
  1300.          * was invoked without the ASYNC parameter and the message
  1301.          * still need to be replied.
  1302.          */
  1303.  
  1304.     SyncMsg = ChildMessage->RexxMsg;
  1305.  
  1306.         /* No error has occured so far. */
  1307.  
  1308.     Error = 0;
  1309.  
  1310.         /* Redirect process output to the console window. */
  1311.  
  1312.     if(Stream = Open("CONSOLE:",MODE_NEWFILE))
  1313.     {
  1314.         SelectOutput(Stream);
  1315.  
  1316.             /* Send the command to the Rexx process. */
  1317.  
  1318.         Error = DoRexxCommand(ChildMessage->Command);
  1319.  
  1320.             /* Close the output stream. */
  1321.  
  1322.         SelectOutput(NULL);
  1323.         Close(Stream);
  1324.     }
  1325.     else
  1326.         Error = IoErr();
  1327.  
  1328.         /* Do we need to reply the RexxMsg? */
  1329.  
  1330.     if(SyncMsg)
  1331.     {
  1332.         if(Error)
  1333.             ReturnErrorMsg(SyncMsg,ChildMessage->PortName,Error);
  1334.         else
  1335.             ReturnRexxMsg(SyncMsg,0);
  1336.     }
  1337.  
  1338.     ReplyMsg(ChildMessage);
  1339. }
  1340.  
  1341.     /* DeleteChildMessage(struct ChildMessage *ChildMessage):
  1342.      *
  1343.      *    Dispose of a message created by CreateChildMessage().
  1344.      */
  1345.  
  1346. VOID
  1347. DeleteChildMessage(struct ChildMessage *ChildMessage)
  1348. {
  1349.     FreeMem(ChildMessage,ChildMessage->Message.mn_Length);
  1350. }
  1351.  
  1352.     /* CreateChildMessage():
  1353.      *
  1354.      *    This routine sets up a custom message to send to a
  1355.      *    child process which gets launched if the RX command
  1356.      *    is invoked with the CONSOLE keyword.
  1357.      */
  1358.  
  1359. struct ChildMessage *
  1360. CreateChildMessage(struct MsgPort *ReplyPort,const STRPTR Command,struct RexxMsg *RexxMessage)
  1361. {
  1362.     UWORD Length;
  1363.     struct ChildMessage *ChildMessage;
  1364.  
  1365.         /* Make room for the message plus the command string. */
  1366.  
  1367.     Length = sizeof(struct ChildMessage) + strlen(Command);
  1368.  
  1369.     ChildMessage = (struct ChildMessage *)AllocMem(Length,MEMF_CLEAR|MEMF_PUBLIC);
  1370.  
  1371.     if(ChildMessage != NULL)
  1372.     {
  1373.             /* Initialize the message. */
  1374.  
  1375.         ChildMessage->Message.mn_Length = Length;
  1376.         ChildMessage->Message.mn_ReplyPort = ReplyPort;
  1377.  
  1378.         strcpy(ChildMessage->Command,Command);
  1379.     }
  1380.  
  1381.     return(ChildMessage);
  1382. }
  1383.  
  1384. LONG
  1385. Cmd_Rx(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1386. {
  1387.     enum    { ARG_CONSOLE,ARG_ASYNC,ARG_COMMAND };
  1388.  
  1389.     struct LocalData *LocalData;
  1390.     struct ChildMessage *ChildMessage;
  1391.     BPTR Stream;
  1392.     struct Process *Child;
  1393.     struct FileHandle *Handle;
  1394.     LONG Error;
  1395.  
  1396.     Printf("RX");
  1397.  
  1398.     if(Args[ARG_CONSOLE] != NULL)
  1399.         Printf(" CONSOLE");
  1400.  
  1401.     if(Args[ARG_ASYNC] != NULL)
  1402.         Printf(" ASYNC");
  1403.  
  1404.     if(Args[ARG_COMMAND] != NULL)
  1405.         Printf(" COMMAND=\"%s\"",Args[ARG_COMMAND]);
  1406.  
  1407.     Printf("\n");
  1408.  
  1409.         /* If no command was invoked, return without
  1410.          * doing any real work.
  1411.          */
  1412.  
  1413.     if(Args[ARG_COMMAND] == NULL)
  1414.     {
  1415.         ReturnRexxMsg(Message,0);
  1416.         return(0);
  1417.     }
  1418.  
  1419.     LocalData = (struct LocalData *)UserData;
  1420.  
  1421.         /* Should we open a console output window? */
  1422.  
  1423.     if(Args[ARG_CONSOLE])
  1424.     {
  1425.         LocalData = (struct LocalData *)UserData;
  1426.  
  1427.             /* Create the process startup message. */
  1428.  
  1429.         ChildMessage = CreateChildMessage(LocalData->GlobalPort,Args[ARG_COMMAND],Message);
  1430.  
  1431.         if(ChildMessage != NULL)
  1432.         {
  1433.                 /* Fill in the name of the Rexx host. */
  1434.  
  1435.             ChildMessage->PortName = LocalData->PortName;
  1436.  
  1437.                 /* If the command should execute synchronously,
  1438.                  * store the RexxMsg pointer in the startup
  1439.                  * message. The background process will then
  1440.                  * return the message after executing the
  1441.                  * command.
  1442.                  */
  1443.  
  1444.             if(Args[ARG_ASYNC] == NULL)
  1445.                 ChildMessage->RexxMsg = Message;
  1446.  
  1447.                 /* Open the output console; you may want to substitute
  1448.                  * the title "Host" in your own programs.
  1449.                  */
  1450.  
  1451.             if(Stream = Open("CON:0/25/640/150/Host/AUTO/WAIT",MODE_NEWFILE))
  1452.             {
  1453.                     /* Turn the BPTR to the file handle into a
  1454.                      * pointer to the file handle.
  1455.                      */
  1456.  
  1457.                 Handle = (struct FileHandle *)BADDR(Stream);
  1458.  
  1459.                     /* Create the background process. */
  1460.  
  1461.                 Child = CreateNewProcTags(
  1462.                     NP_Input,    Stream,
  1463.                     NP_CloseInput,    FALSE,
  1464.                     NP_Output,    NULL,
  1465.                     NP_ConsoleTask,    Handle->fh_Type,
  1466.                     NP_WindowPtr,    -1,
  1467.                     NP_Entry,    RxEntry,
  1468.                 TAG_DONE);
  1469.  
  1470.                 if(Child != NULL)
  1471.                 {
  1472.                         /* Send the startup message. */
  1473.  
  1474.                     PutMsg(&Child->pr_MsgPort,ChildMessage);
  1475.  
  1476.                         /* Increment the number of outstanding
  1477.                          * startup messages.
  1478.                          */
  1479.  
  1480.                     LocalData->Usage++;
  1481.  
  1482.                         /* If the command was to be executed
  1483.                          * asynchronously, return the message
  1484.                          * now so the calling program can
  1485.                          * continue running while the process
  1486.                          * is dealing with the command.
  1487.                          */
  1488.  
  1489.                     if(Args[ARG_ASYNC] != NULL)
  1490.                         ReturnRexxMsg(Message,0);
  1491.  
  1492.                     return(0);
  1493.                 }
  1494.                 else
  1495.                     Error = IoErr();
  1496.  
  1497.                 Close(Stream);
  1498.             }
  1499.             else
  1500.                 Error = IoErr();
  1501.  
  1502.             DeleteChildMessage(ChildMessage);
  1503.         }
  1504.         else
  1505.             Error = IoErr();
  1506.     }
  1507.     else
  1508.     {
  1509.             /* If the command should execute asynchronously,
  1510.              * reply the RexxMsg now.
  1511.              */
  1512.  
  1513.         if(Args[ARG_ASYNC] != NULL)
  1514.             ReturnRexxMsg(Message,0);
  1515.  
  1516.             /* Execute the command. */
  1517.  
  1518.         Error = DoRexxCommand(Args[ARG_COMMAND]);
  1519.  
  1520.             /* If the RexxMsg was already replied,
  1521.              * return now.
  1522.              */
  1523.  
  1524.         if(Args[ARG_ASYNC] != NULL)
  1525.             return(0);
  1526.  
  1527.             /* If no error occured, return the RexxMsg. */
  1528.  
  1529.         if(Error == 0)
  1530.         {
  1531.             ReturnRexxMsg(Message,0);
  1532.             return(0);
  1533.         }
  1534.     }
  1535.  
  1536.     return(Error);
  1537. }
  1538.  
  1539.  
  1540. LONG
  1541. Cmd_New(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1542. {
  1543.     enum    { ARG_PORTNAME };
  1544.  
  1545.     Printf("NEW");
  1546.  
  1547.     if(Args[ARG_PORTNAME] != NULL)
  1548.         Printf(" PORTNAME=\"%s\"",Args[ARG_PORTNAME]);
  1549.  
  1550.     Printf("\n");
  1551.  
  1552.     return(ERROR_ACTION_NOT_KNOWN);
  1553. }
  1554.  
  1555. LONG
  1556. Cmd_Clear(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1557. {
  1558.     enum    { ARG_FORCE };
  1559.  
  1560.     Printf("CLEAR");
  1561.  
  1562.     if(Args[ARG_FORCE] != NULL)
  1563.         Printf(" FORCE");
  1564.  
  1565.     Printf("\n");
  1566.  
  1567.     return(ERROR_ACTION_NOT_KNOWN);
  1568. }
  1569.  
  1570. LONG
  1571. Cmd_Open(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1572. {
  1573.     enum    { ARG_FILENAME,ARG_FORCE };
  1574.  
  1575.     Printf("OPEN");
  1576.  
  1577.     if(Args[ARG_FILENAME] != NULL)
  1578.         Printf(" FILENAME=\"%s\"",Args[ARG_FILENAME]);
  1579.  
  1580.     if(Args[ARG_FORCE] != NULL)
  1581.         Printf(" FORCE");
  1582.  
  1583.     Printf("\n");
  1584.  
  1585.     return(ERROR_ACTION_NOT_KNOWN);
  1586. }
  1587.  
  1588. LONG
  1589. Cmd_Save(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1590. {
  1591.     Printf("SAVE");
  1592.     Printf("\n");
  1593.  
  1594.     return(ERROR_ACTION_NOT_KNOWN);
  1595. }
  1596.  
  1597. LONG
  1598. Cmd_SaveAs(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1599. {
  1600.     enum    { ARG_NAME };
  1601.  
  1602.     Printf("SAVEAS");
  1603.  
  1604.     if(Args[ARG_NAME] != NULL)
  1605.         Printf(" NAME=\"%s\"",Args[ARG_NAME]);
  1606.  
  1607.     Printf("\n");
  1608.  
  1609.     return(ERROR_ACTION_NOT_KNOWN);
  1610. }
  1611.  
  1612. LONG
  1613. Cmd_Close(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1614. {
  1615.     enum    { ARG_FORCE };
  1616.  
  1617.     Printf("CLOSE");
  1618.  
  1619.     if(Args[ARG_FORCE] != NULL)
  1620.         Printf(" FORCE");
  1621.  
  1622.     Printf("\n");
  1623.  
  1624.     return(ERROR_ACTION_NOT_KNOWN);
  1625. }
  1626.  
  1627. LONG
  1628. Cmd_Print(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1629. {
  1630.     enum    { ARG_PROMPT };
  1631.  
  1632.     Printf("PRINT");
  1633.  
  1634.     if(Args[ARG_PROMPT] != NULL)
  1635.         Printf(" PROMPT");
  1636.  
  1637.     Printf("\n");
  1638.  
  1639.     return(ERROR_ACTION_NOT_KNOWN);
  1640. }
  1641.  
  1642. LONG
  1643. Cmd_Cut(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1644. {
  1645.     Printf("CUT");
  1646.     Printf("\n");
  1647.  
  1648.     return(ERROR_ACTION_NOT_KNOWN);
  1649. }
  1650.  
  1651. LONG
  1652. Cmd_Copy(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1653. {
  1654.     Printf("COPY");
  1655.     Printf("\n");
  1656.  
  1657.     return(ERROR_ACTION_NOT_KNOWN);
  1658. }
  1659.  
  1660. LONG
  1661. Cmd_Paste(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1662. {
  1663.     Printf("PASTE");
  1664.     Printf("\n");
  1665.  
  1666.     return(0);
  1667. }
  1668.  
  1669. LONG
  1670. Cmd_Erase(APTR UserData,struct RexxMsg *Message,STRPTR *Args)
  1671. {
  1672.     enum    { ARG_FORCE };
  1673.  
  1674.     Printf("ERASE");
  1675.  
  1676.     if(Args[ARG_FORCE] != NULL)
  1677.         Printf(" FORCE");
  1678.  
  1679.     Printf("\n");
  1680.  
  1681.     return(ERROR_ACTION_NOT_KNOWN);
  1682. }
  1683.  
  1684. /****************************************************************************/
  1685.  
  1686.     /* This is the table which lists the single commands this ARexx
  1687.      * host implements. Each entry lists the name of the command to
  1688.      * invoke, the argument template and the pointer to the routine
  1689.      * which implements the command. The table is terminated by a
  1690.      * NULL command name.
  1691.      */
  1692.  
  1693. struct CmdFunc CommandTable[] =
  1694. {
  1695.     "NEW",        "PORTNAME/K",                Cmd_New,
  1696.     "CLEAR",    "FORCE/S",                Cmd_Clear,
  1697.     "OPEN",        "FILENAME/K,FORCE/S",            Cmd_Open,
  1698.     "SAVE",        "",                    Cmd_Save,
  1699.     "SAVEAS",    "NAME/K",                Cmd_SaveAs,
  1700.     "CLOSE",    "FORCE/S",                Cmd_Close,
  1701.     "PRINT",    "PROMPT/S",                Cmd_Print,
  1702.     "QUIT",        "FORCE/S",                Cmd_Quit,
  1703.     "CUT",        "",                    Cmd_Cut,
  1704.     "COPY",        "",                    Cmd_Copy,
  1705.     "PASTE",    "",                    Cmd_Paste,
  1706.     "ERASE",    "FORCE/S",                Cmd_Erase,
  1707.     "HELP",        "COMMAND,PROMPT/S",            Cmd_Help,
  1708.     "FAULT",    "NUMBER/A/N",                Cmd_Fault,
  1709.     "RX",        "CONSOLE/S,ASYNC/S,COMMAND/F",        Cmd_Rx,
  1710.  
  1711.     NULL,        NULL,                    NULL
  1712. };
  1713.  
  1714. int
  1715. main(int argc,char **argv)
  1716. {
  1717.     struct RexxParseData * ParseData;
  1718.     struct RexxHost * Host;
  1719.     struct LocalData LocalData;
  1720.     struct RexxMsg * RexxMessage;
  1721.     ULONG Signals;
  1722.  
  1723.         /* This program requires AmigaOS v2.04 or higher to run. */
  1724.  
  1725.     if(SysBase->lib_Version < 37)
  1726.     {
  1727.         printf("This program requires AmigaOS v2.04 or higher to run.\n");
  1728.  
  1729.         return(RETURN_FAIL);
  1730.     }
  1731.  
  1732.         /* Open the rexx system library. */
  1733.  
  1734.     RexxSysBase = OpenLibrary(RXSNAME,0);
  1735.  
  1736.     if(RexxSysBase == NULL)
  1737.     {
  1738.         Printf("Could not open %s.\n",RXSNAME);
  1739.  
  1740.         return(RETURN_FAIL);
  1741.     }
  1742.  
  1743.         /* Open the utility library. */
  1744.  
  1745.     UtilityBase = OpenLibrary("utility.library",37);
  1746.  
  1747.     if(UtilityBase == NULL)
  1748.     {
  1749.         Printf("Could not open utility.library v37.\n");
  1750.  
  1751.         CloseLibrary(RexxSysBase);
  1752.  
  1753.         return(RETURN_FAIL);
  1754.     }
  1755.  
  1756.         /* Create the command parser data. */
  1757.  
  1758.     ParseData = CreateRexxParseData();
  1759.  
  1760.     if(ParseData == NULL)
  1761.     {
  1762.         Printf("Error creating rexx parse data.\n");
  1763.  
  1764.         CloseLibrary(RexxSysBase);
  1765.         CloseLibrary(UtilityBase);
  1766.  
  1767.         return(RETURN_FAIL);
  1768.     }
  1769.  
  1770.         /* Finally create the Rexx host. */
  1771.  
  1772.     Host = CreateRexxHost("HOSTDEMO",0,CommandTable,&LocalData);
  1773.  
  1774.     if(Host == NULL)
  1775.     {
  1776.         Printf("Error creating rexx host.\n");
  1777.  
  1778.         DeleteRexxParseData(ParseData);
  1779.  
  1780.         CloseLibrary(RexxSysBase);
  1781.         CloseLibrary(UtilityBase);
  1782.  
  1783.         return(RETURN_FAIL);
  1784.     }
  1785.  
  1786.         /* Set up the local data. This must be done
  1787.          * before the first Rexx command is processed.
  1788.          */
  1789.  
  1790.     LocalData.CommandTable    = CommandTable;
  1791.     LocalData.Usage        = 0;
  1792.     LocalData.GlobalPort    = Host->GlobalPort;
  1793.     LocalData.PortName    = Host->PortName;
  1794.     LocalData.Terminate    = FALSE;
  1795.  
  1796.     Printf("Host name is \"%s\", waiting for commands.\n",Host->PortName);
  1797.  
  1798.         /* Loop until either the QUIT command is executed or
  1799.          * Ctrl-C is pressed.
  1800.          */
  1801.  
  1802.     do
  1803.     {
  1804.             /* Wait for Ctrl-C or an incoming Message. */
  1805.  
  1806.         Signals = Wait(SIGBREAKF_CTRL_C | (1L << Host->GlobalPort->mp_SigBit));
  1807.  
  1808.             /* Ctrl-C signal received? */
  1809.  
  1810.         if(Signals & SIGBREAKF_CTRL_C)
  1811.             LocalData.Terminate = TRUE;
  1812.  
  1813.             /* Message received? */
  1814.  
  1815.         if(Signals & (1L << Host->GlobalPort->mp_SigBit))
  1816.         {
  1817.                 /* Process all incoming messages. */
  1818.  
  1819.             while((RexxMessage = (struct RexxMsg *)GetMsg(Host->GlobalPort)) != NULL)
  1820.             {
  1821.                     /* If the incoming message is indeed
  1822.                      * a RexxMsg, process the command
  1823.                      */
  1824.  
  1825.                 if(IsRexxMsg(RexxMessage))
  1826.                     HandleRexxMsg(Host,ParseData,RexxMessage);
  1827.                 else
  1828.                 {
  1829.                         /* Otherwise it is a startup message
  1830.                          * sent to a background process. We
  1831.                          * need to delete this message and
  1832.                          * decrement the number of outstanding
  1833.                          * startup messages.
  1834.                          */
  1835.  
  1836.                     DeleteChildMessage((struct ChildMessage *)RexxMessage);
  1837.  
  1838.                     LocalData.Usage--;
  1839.                 }
  1840.             }
  1841.         }
  1842.  
  1843.         /* The loop will be terminated when the "Terminate" flag changes
  1844.          * to TRUE and no further startup messages are outstanding.
  1845.          */
  1846.     }
  1847.     while(LocalData.Usage > 0 || LocalData.Terminate == FALSE);
  1848.  
  1849.         /* That's all, folks... */
  1850.  
  1851.     Printf("Cleaning up.\n");
  1852.  
  1853.     DeleteRexxHost(Host);
  1854.     DeleteRexxParseData(ParseData);
  1855.  
  1856.     CloseLibrary(RexxSysBase);
  1857.     CloseLibrary(UtilityBase);
  1858.  
  1859.     return(RETURN_OK);
  1860. }
  1861.